package com.content.scaffold.common.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import com.content.scaffold.common.config.obs.FileOperateHelper;
import com.content.scaffold.common.constants.Constants;
import com.content.scaffold.common.exception.BaseException;
import com.content.scaffold.common.web.ResponseCodeEnum;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.Splitter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author xiaojl
 */
@Slf4j
public class PdfUtils {

    private static final  String ERROR_MSG = "文件合成失败";

    /**
     * @param pdfFile  原要拆的pdf
     * @param dpi      像素越大越慢，建议300
     * @param outPath  拆开之后的照片 父目录 输出路径
     */
    public static  List<String>  convertPdf2Img(File pdfFile, int dpi, String outPath) {
        try (PDDocument document = Loader.loadPDF (pdfFile)) {
          return getTempFile(dpi, outPath, document);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return  Collections.emptyList();
        }
    }

    private static List<String> getTempFile(int dpi, String outPath, PDDocument document) throws IOException {
        PDFRenderer pdfRenderer = new PDFRenderer(document);
        List<String> tempFileList  = Lists.newLinkedList();
        for (int page = 0; page < document.getNumberOfPages(); page++) {
            String outputFileName = outPath + File.separator + (page + 1) + ".jpg";
            File outputDir = new File(outputFileName).getParentFile();
            if (!outputDir.exists()) {
                outputDir.mkdirs();
            }
            BufferedImage bim = pdfRenderer.renderImageWithDPI(page, dpi, ImageType.RGB);
            ImageIO.write(bim, "jpg", new File(outputFileName));
            tempFileList.add(outputFileName);
        }
        return tempFileList;
    }


    /**
     * @param bytes  原要拆的pdf
     * @param dpi      像素越大越慢，建议300
     * @param outPath  拆开之后的照片 父目录 输出路径
     */
    public static  List<String>  convertPdf2Img(byte[] bytes, int dpi, String outPath) {
        try (PDDocument document = Loader.loadPDF (bytes)) {
           return  getTempFile(dpi, outPath, document);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return  Collections.emptyList();
        }
    }

    public static  List<String>  convertPdf2Img(byte[] bytes,  String outPath) {
        try (PDDocument document = Loader.loadPDF (bytes)) {
            return  getTempFile(300, outPath, document);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return  Collections.emptyList();
        }
    }

    /**
     *
     * @param sourceFile  原文件的pdf
     * @param outPath 输出的文件
     * @param dpi 分辨率
     * @param startPage 第几页开始
     * @param endPage  第几页结束
     */
    public static  List<String> spiltPdf2Img(File sourceFile,String outPath,Integer dpi,Integer startPage,Integer endPage)  {
        // Loading an existing PDF document
        try ( PDDocument document = Loader.loadPDF(sourceFile)){
            // Instantiating Splitter class
          return   getSpiltTempFile(outPath, dpi, startPage, endPage, document);
        }catch (Exception e){
            log.error("拆分PDF失败",e);
            return   Collections.emptyList();
        }

    }

    public static  List<String> spiltPdf2Img(byte[] sourceFile,String outPath,Integer dpi,Integer startPage,Integer endPage)  {
        // Loading an existing PDF document
        try ( PDDocument document = Loader.loadPDF(sourceFile)){
          return   getSpiltTempFile(outPath, dpi, startPage, endPage, document);
        }catch (Exception e){
            log.error("拆分PDF失败",e);
            return Collections.emptyList();
        }

    }

    public static  List<String> spiltPdf2Img(byte[] bytes,String outPath,Integer startPage,Integer endPage){
        return spiltPdf2Img(bytes,outPath,300,startPage,endPage);
    }

    private static   List<String>  getSpiltTempFile(String outPath, Integer dpi, Integer startPage, Integer endPage, PDDocument document) throws IOException {

        List<String> tempFileList  = Lists.newLinkedList();
        // Instantiating Splitter class
        Splitter splitter = new Splitter();
        splitter.setStartPage(startPage);
        splitter.setSplitAtPage(1);
        splitter.setEndPage(endPage);

        // splitting the pages of a PDF document
        List<PDDocument> pages = splitter.split(document);
        // Creating an iterator
        Iterator<PDDocument> iterator = pages.listIterator();

        // Saving each page as an individual document
        int j = startPage;
        while (iterator.hasNext()) {
            PDDocument pd = iterator.next();
            String pdfName = j++ + ".jpg";
            String outputFileName = outPath + File.separator + pdfName;
            File outputDir = new File(outputFileName).getParentFile();
            if (!outputDir.exists()) {
                outputDir.mkdirs();
            }
            PDFRenderer pdfRenderer = new PDFRenderer(pd);
            BufferedImage bim = pdfRenderer.renderImageWithDPI(0, dpi, ImageType.RGB);

            ImageIO.write(bim, "jpg", new File(outputFileName));

            tempFileList.add(outputFileName);
        }
        return tempFileList;
    }




    public static File takeAppointPage(File sourceFile,String outPath,Integer dpi,Integer pageIndex) throws IOException {
        try (PDDocument document = Loader.loadPDF(sourceFile)){
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            BufferedImage bim = pdfRenderer.renderImageWithDPI(pageIndex, dpi, ImageType.RGB);
            File  file  =new File(outPath);
            ImageIO.write(bim, "jpg", file);
            return  file;
        }
    }

    public static File takeAppointPage(File sourceFile,String outPath,Integer pageIndex)throws IOException{
        return takeAppointPage(sourceFile,outPath,300,pageIndex);
    }

    public static byte[] takeAppointPage(byte[] bytes , Integer dpi,Integer pageIndex) throws IOException {
        try (PDDocument document = Loader.loadPDF(bytes)){
            if (pageIndex >= document.getNumberOfPages()){
                pageIndex = document.getNumberOfPages() - 1;
            }
            if (pageIndex < 0){
                pageIndex = 0;
            }
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            BufferedImage bim = pdfRenderer.renderImageWithDPI(pageIndex, dpi, ImageType.RGB);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(bim, "jpg", out);
            return  out.toByteArray();
        }
    }
    public static byte[] takeAppointPage(byte[] bytes ,Integer pageIndex) throws IOException
    {
        return  takeAppointPage(bytes,300,pageIndex-1);
    }


    public static File takeFirstPage(File sourceFile,String outPath,Integer dpi) throws IOException {
        try (PDDocument document = Loader.loadPDF(sourceFile)){
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            BufferedImage bim = pdfRenderer.renderImageWithDPI(0, dpi, ImageType.RGB);
            File  file  =new File(outPath);
            ImageIO.write(bim, "jpg", file);
            return  file;
        }
    }

    public static byte[] takeFirstPage(byte[] bytes , Integer dpi) throws IOException {
        try (PDDocument document = Loader.loadPDF(bytes)){
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            BufferedImage bim = pdfRenderer.renderImageWithDPI(0, dpi, ImageType.RGB);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(bim, "jpg", out);
            return  out.toByteArray();
        }
    }

    public static byte[] takeFirstPage(byte[] bytes ) throws IOException{
       return takeFirstPage(bytes,300);
    }

    public static int getTotalPage(File file){
        try (PDDocument document = Loader.loadPDF(file);){
            return  document.getNumberOfPages();
        } catch (IOException e) {
            log.error("文件可能不存在",e);
            return 0;
        }
    }
    public static int getTotalPage(byte[] file){
        try (PDDocument document = Loader.loadPDF(file)){
            return  document.getNumberOfPages();
        } catch (IOException e) {
            log.error("文件可能不存在",e);
            return 0;
        }

    }

    public static byte[] mergePdf(List<String> fileUrl)  {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        try( PDDocument document = new PDDocument();){
            for (String url :fileUrl){
                String fileType = FileUtil.getSuffix(url);
                if ( Constants.IMG_SUFFIX.contains(StringUtils.upperCase(fileType))){
                    PDPage page = new PDPage(PDRectangle.A4);
                    document.addPage(page);
                    ByteArrayInputStream bits = new ByteArrayInputStream(FileOperateHelper.urlToByteArray(url));
                    PDImageXObject image = LosslessFactory.createFromImage(document, ImageIO.read(bits));
                    double originalWidth = image.getWidth();
                    double originalHeight = image.getHeight();
                    PDPageContentStream contents = new PDPageContentStream(document, page);
                    // 计算缩放比例
                    double scale = Math.min(PDRectangle.A4.getWidth() / originalWidth, PDRectangle.A4.getHeight() / originalHeight);

                    // 使用缩放比例设置图片大小
                    double newWidth = originalWidth * scale;
                    double newHeight = originalHeight * scale;
                    contents.drawImage(image, 0, 0, Convert.toFloat(newWidth), Convert.toFloat(newHeight));
                    contents.close();
                }

            }
            document.save(bout);
        }catch (IOException e){
            log.error(ERROR_MSG,e);
            throw  new BaseException(ResponseCodeEnum.ERROR,ERROR_MSG);
        }
        return bout.toByteArray();
    }


    public static File mergePdf(String dir ,String outPath) {
        File file =  new File(outPath);
        try( PDDocument document = new PDDocument()){
            Path directory = Paths.get(dir);
            try (Stream<Path> paths = Files.list(directory)) {
                paths.forEach(subPath -> {
                    PDPageContentStream contents = null;
                    try {
                        if (Files.isRegularFile(subPath)) {
                            File imageFile = subPath.toFile();
                            PDPage page = new PDPage(PDRectangle.A4);
                            document.addPage(page);
                            PDImageXObject image = PDImageXObject.createFromFileByExtension(imageFile, document);//文件形式
                            double originalWidth = image.getWidth();
                            double originalHeight = image.getHeight();
                            contents = new PDPageContentStream(document, page);
                            // 计算缩放比例
                            double scale = Math.min(PDRectangle.A4.getWidth() / originalWidth, PDRectangle.A4.getHeight() / originalHeight);

                            // 使用缩放比例设置图片大小
                            double newWidth = originalWidth * scale;
                            double newHeight = originalHeight * scale;
                            contents.drawImage(image, 0, 0, Convert.toFloat(newWidth), Convert.toFloat(newHeight));
                        }
                    } catch (IOException e) {
                        log.error(ERROR_MSG, e);
                        throw new BaseException(ResponseCodeEnum.ERROR, ERROR_MSG);
                    } finally {
                        IoUtil.close(contents);
                    }
                });
            }
            document.save(file);
        }catch (Exception e){
            log.error(ERROR_MSG,e);
              throw  new BaseException(ResponseCodeEnum.ERROR,ERROR_MSG);
        }
        return  file;

    }
}
